#include <LiquidCrystal.h>
#include <DHT.h>

String data = ""; // CPU占用率
float ratio = 0;
int count = 0; // 电脑回传CPU占用率，若连续10次未收到则LED常亮
int heartbeatsCount = 0; // 记录灯闪烁的次数，每2次获取一次DHT信息


// LED针脚定义
const int led = 13;

// LCD引脚定义
const int rs = 3, en = 4, d4 = 9, d5 = 10, d6 = 11, d7 = 12;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// DHT11引脚及类型定义
DHT dht(8, DHT11);

void setup() {
  // 初始化串口
  Serial.begin(9600);
  // 初始化LED
  pinMode(led, OUTPUT);
  digitalWrite(led, HIGH);
  // 初始化LCD
  lcd.begin(16, 2);
  // 初始化DHT
  dht.begin();
  Serial.println("Heartbeats is ready");
}

void loop() {
  ratio = getRatio();
  if (isValid(ratio)) {
    heartbeats(ratio);
    // 每闪烁2次获取一次传感器信息
    if (++heartbeatsCount >= 2) {
      refreshDHT();
      heartbeatsCount = 0;
    }
  } else {
    digitalWrite(led, HIGH);
    // 当灯不闪烁时，每2s获取一次传感器信息
    delay(2000);
    refreshDHT();
  }
  data = "";
}

// 根据ratio亮灯
void heartbeats(float ratio) {
  // 控制PWM亮度的增加
  for (int a = 0; a <= 255; a++) {
    analogWrite(led, a);
    delayMicroseconds(5000 * ratio);
  }
  // 控制PWM亮度减小
  for (int a = 255; a >= 0; a--) {
    analogWrite(led, a);
    delayMicroseconds(5000 * ratio);
  }
  delay(800 * ratio);
}

// 从串口读取字符串数据，以#结束，输出ratio
float getRatio() {
  // 读取字符串
  while (Serial.available() > 0) {
    char ch = char(Serial.read());
    if (ch == '#') {
      break;
    }
    data += ch;
    delay(2);
  }
  // 转换为ratio
  if (data != "") {
    count = 0;
    Serial.print("CPU: ");
    Serial.println(data.toInt());
    float tempRatio = (110.0 - data.toInt()) / 100.0;
    if (isValid(tempRatio)) {
      Serial.print("RATIO IS SET TO: ");
      Serial.println(tempRatio);
      lcdPrint("CPU:" + data + "%", 1, 1);
      return tempRatio;
    } else {
      Serial.println("!!!INVALID RATIO!!!");
      return 0;
    }
  } else {
    // 没收到信息超过10个循环，就让灯常亮
    if (++count >= 10) {
      Serial.println("!!!COMMUNICATION FAINED!!!");
      lcdPrint("CPU:Failed!", 1, 1);
      return 0;
    }
  }
}

// 检验ratio是否在合理范围内
boolean isValid(float ratio) {
  return ratio >= 0.1 && ratio <= 1.1;
}

// LCD输出，行列从1开始
void lcdPrint(String s, int row, int column) {
  lcd.setCursor(column - 1, row - 1);
  lcd.print(s);
}

// 更新DHT传感器信息
void refreshDHT() {
  // 读温度或湿度要用250毫秒
  int h = dht.readHumidity();
  int t = dht.readTemperature();
  String s = "TEM:" + String(t) + (char)223 +"C HUM:" + String(h) + "%";
  Serial.println(s);
  lcdPrint(s, 2, 1);
  if (pleasantTemperature(t) && pleasantHumidity(h)) {
    lcdPrint("^_^", 1, 13);
  } else if (pleasantTemperature(t) || pleasantHumidity(h)) {
    lcdPrint("0_0", 1, 13);
  } else {
    lcdPrint("T_T", 1, 13);
  }
}

// 判断温度是否在合适范围内
boolean pleasantTemperature(int t) {
  return t >= 19 && t <= 27;
}

// 判断湿度是否在合适范围内
boolean pleasantHumidity(int h) {
  return h >= 20 && h <= 85;
}
